/**
 * 
 */
package uk.ac.manchester.cs.graph.io;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import uk.ac.manchester.cs.graph.Edge;
import uk.ac.manchester.cs.graph.Graph;

/**
 * @author Pavel Klinov
 *
 */
public class  GraphMLWriterImpl<V,E> implements GraphWriter<V, E> {


	
	private Document m_lastDoc = null;
	
	private GraphNodeWriter<V> m_nodeWriter = new GraphNodeWriter<V>() {
		
		public void write(V vertex, Writer out) throws IOException  {
			
			out.write( vertex.toString() );
		}
	};
	
	public Document getLastDocument() {
		return m_lastDoc;
	}
	
	@Override
	public void write(Graph<V, E> graph, Writer writer) throws IOException {

		try {
			
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			Document graphmlDoc = builder.newDocument();
			Element rootElem = graphmlDoc.createElement(GraphMLConstants.ROOT_ELEMENT);
			
			graphmlDoc.appendChild(rootElem);
			
			Element graphElem = graphmlDoc.createElement(GraphMLConstants.GRAPH_ELEMENT);
			
			rootElem.appendChild(graphElem);
			graphElem.setAttribute(GraphMLConstants.DIRECTED_ATTRIBUTE, graph.isDirected() ? GraphMLConstants.EDGE_DIRECTED_VALUE : GraphMLConstants.EDGE_UNDIRECTED_VALUE);
			//Now serializing nodes
			for (V node : graph.getVertices()) {
				Element nodeElem = graphmlDoc.createElement(GraphMLConstants.NODE_ELEMENT);

				nodeElem.setAttribute(GraphMLConstants.NODE_ID_ATTRIBUTE, node2string(node));
				graphElem.appendChild(nodeElem);
			}
			//Next, edges
			for (V node : graph.getVertices()) {
				for (Edge<V,E> edge : graph.getIncidentEdges(node)) {
					Element edgeElem = graphmlDoc.createElement(GraphMLConstants.EDGE_ELEMENT);

					edgeElem.setAttribute(GraphMLConstants.NODE_SOURCE_ATTRIBUTE, node2string(edge.getStart()));
					edgeElem.setAttribute(GraphMLConstants.NODE_TARGET_ATTRIBUTE, node2string(edge.getEnd()));
					graphElem.appendChild(edgeElem);
				}
			}
			//Now write this out
			TransformerFactory transfac = TransformerFactory.newInstance();
            Transformer trans = transfac.newTransformer();
            
            trans.setOutputProperty(OutputKeys.INDENT, "yes");

            StreamResult result = new StreamResult(writer);
            DOMSource source = new DOMSource(graphmlDoc);
            
            trans.transform(source, result);
            m_lastDoc =  graphmlDoc;
			
		} catch (Throwable e) {
			throw new IOException(e);
		}
	}

	private String node2string(V node) throws IOException {
		StringWriter strWriter = new StringWriter();
		
		m_nodeWriter.write(node, strWriter);
		
		return strWriter.getBuffer().toString();
	}
	
	@Override
	public void setNodeWriter(GraphNodeWriter<V> writer) {
		m_nodeWriter = writer;
	}
}
